﻿using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
using System.Threading;
using System.Collections.Generic;
using UDPHelper;

namespace AMInstantMessaging
{
    public class UDPClientHelper
    {
        private UDPClientHelper() { }

        private static UDPClientHelper _helper = null;
        private static object _lock = new object();

        private UDPManager _UDPManager = null;

        public static UDPClientHelper GetInstance()
        {
            if (_helper == null) lock (_lock) if (_helper == null)
                    {
                        _helper = new UDPClientHelper();
                        _helper._UDPManager = new UDPManager();
                        _helper._UDPManager.AddReceiveCallback(_helper.ReceiveCallback);
                    }

            return _helper;
        }

        /// <summary>
        /// 接收回调
        /// </summary>
        /// <param name="package"></param>
        private void ReceiveCallback(UDPPackage package)
        {
            // 判定消息种类
            var package_id = UDPPackageHelper.GetHeader(package, "PackageId");
            if (string.IsNullOrEmpty(package_id))
                return;

            PackageId2Response.TryAdd(package_id, package);

            if (PackageId2ResetEvent.TryGetValue(package_id, out ManualResetEvent reset_event))
            {
                reset_event.Set();
            }
            else
            {
                PackageId2Response.TryRemove(package_id, out package);
            }
        }

        /// <summary>
        /// 服务地址
        /// </summary>
        /// <returns></returns>
        public IPEndPoint GetServerIPEndPoint()
        {
            string ip = ConfigurationManager.AppSettings["SystemData::ServerIP"] ?? "127.0.0.1";
            int.TryParse(ConfigurationManager.AppSettings["SystemData::ServerPort"] ?? "0", out int port);
            return new IPEndPoint(IPAddress.Parse(ip), port);
        }

        /// <summary>
        /// 开始服务器
        /// </summary>
        public void Start()
        {
            _UDPManager.Start();
        }

        /// <summary>
        /// 停止服务
        /// </summary>
        public void Stop()
        {
            _UDPManager.Stop();
        }

        /// <summary>
        /// 是否正在运行
        /// </summary>
        public bool IsRuning { get { return _UDPManager.IsRuning; } }

        /// <summary>
        /// 发送给服务器
        /// （强制执行发送地址为服务器，忽略包中的设置）
        /// </summary>
        /// <param name="package"></param>
        public void SendPackageToServer(UDPPackage package)
        {
            if (package == null)
                return;

            var point = GetServerIPEndPoint();
            package.IPAddress = point.Address;
            package.Port = point.Port;

            _UDPManager.UDPPackageManager.PushUDPPackageToStack(package);
        }

        private ConcurrentDictionary<string, ManualResetEvent> PackageId2ResetEvent = new ConcurrentDictionary<string, ManualResetEvent>();
        private ConcurrentDictionary<string, UDPPackage> PackageId2Response = new ConcurrentDictionary<string, UDPPackage>();

        /// <summary>
        /// 消息的Id
        /// </summary>
        private long PackageId = 0;

        /// <summary>
        /// 发送消息并且等待响应
        /// </summary>
        /// <param name="package"></param>
        /// <param name="waittime"></param>
        /// <returns></returns>
        public UDPPackage SendPackageToServerAndWaitResponse(object package, int waittime = 5000)
        {
            return SendPackageToServerAndWaitResponse(new UDPPackage { Body = package }, waittime);
        }

        /// <summary>
        /// 发送消息并且等待响应
        /// </summary>
        /// <param name="package"></param>
        /// <returns></returns>
        public UDPPackage SendPackageToServerAndWaitResponse(UDPPackage package, int waittime = 5000)
        {
            string current_id = (++PackageId).ToString();

            if (package.Header == null)
                package.Header = new Dictionary<string, string>();

            package.Header["PackageId"] = current_id;

            ManualResetEvent reset_event = new ManualResetEvent(false);
            PackageId2ResetEvent.TryAdd(current_id, reset_event);
            SendPackageToServer(package);
            reset_event.WaitOne(waittime);

            PackageId2ResetEvent.TryRemove(current_id, out reset_event);
            PackageId2Response.TryRemove(current_id, out UDPPackage rpackage);
            return rpackage;
        }
    }
}
